#pragma once

#include "nef_export.h"
#include <string>
#include "resolve_type.h"
#include <typeindex>

namespace nef {

template<typename T>
struct is_export_match
{
	static_assert(sizeof(T) == 0, "This can only be used by an export definition");

	static bool Check(const std::string &contractName, const std::type_index &idx)
	{
		return false;
	}
};

template<typename Type, typename IxType, const char *ContractName, typename scope>
struct is_export_match<nef_export<Type, IxType, ContractName, scope>>
{
	
	static bool Check(const std::string &contractName, const std::type_index &idx)
	{
		static std::type_index TYPE_INDEX = std::type_index(typeid(IxType));
		static const std::string CONTRACT_NAME = ContractName;

		return idx == TYPE_INDEX &&
			contractName == CONTRACT_NAME;
	}
};

template<typename Owner, typename IxType, OWNER_GETTER, const char *ContractName, typename scope>
struct is_export_match<nef_export_memfun<Owner, IxType, F, ContractName, scope>>
{
	static bool Check(const std::string &contractName, const std::type_index &idx)
	{
		static std::type_index TYPE_INDEX = std::type_index(typeid(IxType));
		static const std::string CONTRACT_NAME = ContractName;

		return idx == TYPE_INDEX &&
			contractName == CONTRACT_NAME;
	}
};

#define PP_DECLARE_EXPORT_MATCH(z, n, _) \
	\
	template<typename T, bool val> \
struct resolve_export_match_ ## n ## _t \
{ \
	static bool Check(const std::string &contractName, const std::type_index &idx) { return false; } \
}; \
	\
	template<typename T> \
struct resolve_export_match_ ## n ## _t<T, true> \
{ \
	static bool Check(const std::string &contractName, const std::type_index &idx) \
{ \
	return is_export_match<typename T::export ## n>::Check(contractName, idx); \
} }; \
	\
	template<typename T> \
struct resolve_export_match_ ## n \
	: resolve_export_match_ ## n ## _t<T, has_export ## n ## _test<T>::value> \
{};

BOOST_PP_REPEAT(MAX_EXPORTS, PP_DECLARE_EXPORT_MATCH, _)


	template<typename T>
bool has_export_match(const std::string &contractName, const std::type_index &idx)
{
#define PP_OR() ||
#define PP_CHECK_N(z, C, _) BOOST_PP_IF(C, PP_OR, BOOST_PP_EMPTY)() resolve_export_match_ ## C<T>::Check(contractName, idx)

	return BOOST_PP_REPEAT(MAX_EXPORTS, PP_CHECK_N, _);

#undef PP_CHECK_N
#undef PP_OR
}

}